package com.lj.wordmark.mark;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.lj.wordmark.utils.WordMarkUtils;
import org.apache.poi.wp.usermodel.HeaderFooterType;
import org.apache.poi.xwpf.usermodel.*;

import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.List;
import java.util.Map;

/**
 * @author luojing
 * @date 2024/3/15
 */
public class WordMark implements Closeable {

    //文档对象
    private final XWPFDocument document;

    public static final String START_STR = "{{";
    public static final String END_STR = "}}";

    public WordMark(String url) throws IOException {
        this(Files.newInputStream(Paths.get(url)), true);
    }

    public WordMark(InputStream in, boolean closeStream) throws IOException {
        document = new XWPFDocument(in);
        // 这个过程是很耗时的，所以当该对象创建出来后，应该会被缓存
        this.parseDocument(document);
        if (closeStream) {
            IoUtil.close(in);
        }
    }

    private void parseDocument(XWPFDocument document) {
        // 解析段落
        this.parseParagraphs(document);
        // 解析页头和页脚
        this.parseHeaderAndFoot(document);
        // 解析表格
        this.parseTable(document);
    }

    private void parseTable(XWPFDocument document) {
    }

    private void parseHeaderAndFoot(XWPFDocument document) {
        for (XWPFHeader xwpfHeader : document.getHeaderList()) {
            for (XWPFParagraph paragraph : xwpfHeader.getParagraphs()) {
                this.parseParagraph(paragraph);
            }
        }
        for (XWPFFooter xwpfFooter : document.getFooterList()) {
            for (XWPFParagraph paragraph : xwpfFooter.getParagraphs()) {
                this.parseParagraph(paragraph);
            }
        }
    }

    private void parseParagraphs(XWPFDocument document) {
        List<XWPFParagraph> paragraphs = document.getParagraphs();
        if (CollUtil.isEmpty(paragraphs)) {
            return;
        }
        for (XWPFParagraph paragraph : paragraphs) {
            this.parseParagraph(paragraph);
        }
    }

    private void parseParagraph(XWPFParagraph paragraph) {
        if (!paragraph.getText().contains(START_STR)) {
            return;
        }
        Deque<XWPFRun> deque = new ArrayDeque<>();
        boolean start = false;
        // 始终指向当前要索引的Run对象
        int index = 0;
        while (index < paragraph.getRuns().size()) {
            XWPFRun currentRun = paragraph.getRuns().get(index);
            deque.addLast(currentRun);
            StringBuilder sb = new StringBuilder();
            for (XWPFRun xwpfRun : deque) {
                sb.append(xwpfRun.getText(0));
            }
            String text = sb.toString();
            // 判断是否遇到了开始
            if (!start && text.contains(START_STR)) {
                // 当前是在探索开始
                start = true;
            } else {
                if (text.contains(END_STR)) {
                    start = false;
                    // 提取标记
                    index = extractMark(paragraph, index, deque, text);
                }
            }
            if (start) {
                // 判断是当前文本遇到的还是和上一个文本相加才遇到的
                int i = text.indexOf(START_STR);
                if (deque.peekFirst().getText(0).length() <= i) {
                    deque.removeFirst();
                }
                // 是否遇到了结束 防止当前文本携带了结束标签
                if (text.contains(END_STR)) {
                    start = false;
                    // 提取标记
                    index = extractMark(paragraph, index, deque, text);
                }
            } else if (deque.size() >= 2) {
                deque.removeFirst();
            }
            index++;
        }
    }

    private int extractMark(XWPFParagraph paragraph, int index, Deque<XWPFRun> deque, String text) {
        // 处理开头
        if (!text.startsWith(START_STR)) {
            XWPFRun first = deque.peekFirst();
            String firstText = first.getText(0);
            int i = text.indexOf(START_STR);
            XWPFRun xwpfRun = paragraph.insertNewRun(index - deque.size() + 1);
            // 记得索引+1
            index++;
            WordMarkUtils.copyRun(first, xwpfRun, firstText.substring(0, i));
            first.setText(firstText.substring(i), 0);
        }
        // 处理结尾 防止有多个占位粘连在一起
        XWPFRun last = deque.peekLast();
        String tempText = text;
        String lastText = last.getText(0);
        int i;
        boolean flag = true;
        while ((i = tempText.indexOf(END_STR)) > 0) {
            if (flag) {
                // 最后一个文本的
                int num = tempText.length() - i - END_STR.length();
                last.setText(lastText.substring(0, lastText.length() - num), 0);
                lastText = lastText.substring(lastText.length() - num);
                tempText = tempText.substring(i + END_STR.length());
                flag = false;
            } else {
                // 多个标记粘连在一起
                int j;
                if ((j = tempText.indexOf(START_STR)) >= 0) {
                    // abc}}aa{{bbb}}cc 这种提取aa
                    if (j > 0) {
                        XWPFRun xwpfRun = paragraph.insertNewRun(index + 1);
                        index++;
                        WordMarkUtils.copyRun(last, xwpfRun, lastText.substring(0, j));
                    }
                    // 提取{{bbb}}
                    XWPFRun xwpfRun = paragraph.insertNewRun(index + 1);
                    index++;
                    WordMarkUtils.copyRun(last, xwpfRun, lastText.substring(j, i + END_STR.length()));
                    tempText = tempText.substring(i + END_STR.length());
                    lastText = lastText.substring(i + END_STR.length());
                }
            }
        }
        // 开始提取
        XWPFRun first = null;
        StringBuilder sb = new StringBuilder();
        for (XWPFRun run : deque) {
            sb.append(run.getText(0));
            if (first == null) {
                first = run;
            } else {
                // 将中间的碎片化的文本设置为空
                run.setText("", 0);
            }
        }
        deque.clear();
        first.setText(sb.toString(), 0);
        // 防止末尾还有数据，abc}}aa{{bbb}}c{    不知道c{会不会是下一个标志位的开始 所以将最后的残留物进队列
        if (StrUtil.isNotEmpty(lastText)) {
            XWPFRun xwpfRun = paragraph.insertNewRun(index + 1);
            WordMarkUtils.copyRun(last, xwpfRun, lastText);
        }
        return index;
    }

    public void doWrite(OutputStream out, Object sourceObject) {
        // todo 资源数据进行校验 只接受对象或者map类型
        // 创建一个新的文档，将之前解析的文档拷贝并根据资源数据进行解析
        try (XWPFDocument xwpfDocument = new XWPFDocument()) {
            copyDocument(xwpfDocument, sourceObject);
            xwpfDocument.write(out);
            out.flush();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private void copyDocument(XWPFDocument xwpfDocument, Object sourceObject) {
        // 拷贝内容 记住一定要按照顺序拷贝
        xwpfDocument.setCommitted(document.isCommitted());
        xwpfDocument.setEvenAndOddHeadings(document.getEvenAndOddHeadings());
        xwpfDocument.setMirrorMargins(document.getMirrorMargins());
        xwpfDocument.setTrackRevisions(document.isTrackRevisions());
        xwpfDocument.setZoomPercent(document.getZoomPercent());
        List<IBodyElement> bodyElements = this.document.getBodyElements();
        for (IBodyElement bodyElement : bodyElements) {
            if (bodyElement instanceof XWPFParagraph) {
                XWPFParagraph xwpfParagraph = xwpfDocument.createParagraph();
                WordMarkUtils.copyParagraph((XWPFParagraph) bodyElement, xwpfParagraph, sourceText -> parseRunText(sourceText, sourceObject));
            }
            if (bodyElement instanceof XWPFTable) {
//                xwpfDocument.createTable()
            }
        }
        // 拷贝页头页脚
        for (XWPFHeader header : document.getHeaderList()) {
            XWPFHeader xwpfHeader = xwpfDocument.createHeader(HeaderFooterType.DEFAULT);
            for (XWPFParagraph paragraph : header.getParagraphs()) {
                XWPFParagraph xwpfParagraph = xwpfHeader.createParagraph();
                WordMarkUtils.copyParagraph(paragraph, xwpfParagraph, sourceText -> parseRunText(sourceText, sourceObject));
            }
        }
        for (XWPFFooter footer : document.getFooterList()) {
            XWPFFooter xwpfFooter = xwpfDocument.createFooter(HeaderFooterType.DEFAULT);
            for (XWPFParagraph paragraph : footer.getParagraphs()) {
                XWPFParagraph xwpfParagraph = xwpfFooter.createParagraph();
                WordMarkUtils.copyParagraph(paragraph, xwpfParagraph, sourceText -> parseRunText(sourceText, sourceObject));
            }
        }
    }

    private String parseRunText(String sourceText, Object sourceObject) {
        // 不是标志位的直接返回
        if (!isMark(sourceText)) {
            return sourceText;
        }
        sourceText = shucking(sourceText);
        String[] paths = sourceText.split("\\.");
        return parseRunText(sourceText, sourceObject, paths, 0);
    }

    private String parseRunText(String sourceText, Object sourceObject, String[] paths, int index) {
        if (index >= paths.length) {
            return sourceObject == null ? "" : sourceObject.toString();
        }
        String path = paths[index];
        Object value;
        if (sourceObject instanceof Map) {
            value = ((Map) sourceObject).get(path);
        } else {
            value = ReflectUtil.getFieldValue(sourceObject, path);
        }
        return parseRunText(sourceText, value, paths, index + 1);
    }

    private boolean isMark(String text) {
        if (StrUtil.isBlank(text)) {
            return false;
        }
        return text.startsWith(START_STR) && text.endsWith(END_STR);
    }

    private String shucking(String mark) {
        if (isMark(mark)) {
            return mark.replace(START_STR, "").replace(END_STR, "").trim();
        }
        return mark;
    }


    @Override
    public void close() throws IOException {
        if (document != null) {
            document.close();
        }
    }
}
